/*
 *	Copyright © 2008 University of Houston
 *	All rights reserved.
 */

#include <stdlib.h>
#include <string>
#include <time.h>
#include <iostream>
#include "logger.h"
#include "session.h"

using namespace std;

packetLogger::packetLogger()
{
	handleToFile = NULL;
}

packetLogger::~packetLogger()
{
	if (isInitialized())
		fclose(handleToFile);
}

string packetLogger::formatPacketForLogging(timedHeader h)
{
	// 1kb buffer 'should be enough for anyone!'
	static char buffer[1024];
	string		flags, srcIP, dstIP;

	srcIP = inet_ntoa(h.getSourceIP());
	dstIP = inet_ntoa(h.getDestIP());

	// Print the packet data
	sprintf(&buffer[0],
			format_string(),
			h.getTimestamp().tv_sec,
			h.getTimestamp().tv_usec,
			h.getTCP()->th_seq,
			h.getTCP()->th_ack,
			srcIP.c_str(),
			h.getSourcePort(),
			dstIP.c_str(),
			h.getDestPort(),
			h.getPayloadSize(),
			h.getTCP()->th_flags);

	return (&buffer[0]);
}

void packetLogger::logSession(const session *s)
{
	// Make sure everything is working properly
	if (!isInitialized())
		return;

	// Get our date and time in a buffer...
	const time_t hold_time = time(NULL);
	string logDate = ctime(&hold_time);
	fprintf(handleToFile, "\n#LOG TIME %s", logDate.c_str());

	// Print the session ID number
	fprintf(handleToFile, packet_identifier_string(), s->getSessionID());

	// Create string outside the loop so they are not recreated
	// each iteration...
	string 	printed;
	int 	numPackets = s->getNumPackets();

	for (int i=0; i < numPackets; i++)
	{
		printed = formatPacketForLogging((*s)[i]);
		fprintf(handleToFile, "%s", printed.c_str());
	}

}
bool packetLogger::init(string fileName, string access)
{
	handleToFile = fopen(fileName.c_str(), access.c_str());
	if (!isInitialized())
		return false;
	return true;
}

bool packetLogger::isInitialized()
{
	return (handleToFile != NULL);
}


bool packetLogger::isLogData(const char *buffer) const
{
	// Check for comments...
	if (strchr(buffer, '#') != NULL)
		return false;

	// Check for completely empty lines
	if (buffer[0] == '\n')
		return false;

	// Check for lines that are likely too short to contain any useful
	// data.
	if (strlen(buffer) < (unsigned)MIN_READ_SIZE)
		return false;

	return true;
}
bool packetLogger::isSessionIdHeader(const char *buffer) const
{
	if (strchr(buffer, '@') != NULL)
		return true;
	return false;
}
int packetLogger::getLastSessionNumber()
{
	return getLastSessionNumber(handleToFile);
}
int packetLogger::getLastSessionNumber(FILE *pFile)
{
	// Store our original place in the file, so that we can return to that
	// same location when we are done. fgetpos returns 0 on success
	fpos_t originalPosition;
	if (fgetpos(pFile, &originalPosition) != 0)
		return -1;

	// Seek to the beginning of the file
	rewind(pFile);

	// Keep seeking until we can't get to the next session number.
	int	sessionNumber = 0;
	while (seekToSession(pFile, sessionNumber))
		sessionNumber++;

	// Cleanup
	fsetpos(pFile, &originalPosition);

	// Whatever the most recent session number was, return that number.
	return sessionNumber;
}
bool packetLogger::seekToSession(FILE *pFile, int sessionID)
{

	// Buffers
	const int 	bufferSize = 1024;
	char 		*buffer = new char[bufferSize];
	int			sessionNumber = 0;
	bool		bFound = false;

	// Restore the original position if we cannot find the right place.
	fpos_t originalPosition;
	if (fgetpos(pFile, &originalPosition) != 0)
		return false;

	// Seek to beginning of file
	rewind(pFile);

	// Keep reading until we hit the end of the file
	while (!feof(pFile))
	{
		// Read a line
		if (fgets(buffer, bufferSize, pFile) == NULL)
			break;

		// If this line does not have information on the session number,
		// just skip it.
		if (!isSessionIdHeader(buffer))
			continue;

		// It *does* have information.  Grab the session number.
		if (sscanf(buffer, packet_identifier_string(), &sessionNumber) < 1)
			ERROR("Error reading session number with fscanf")

		if (sessionNumber == sessionID)
		{
			bFound = true;
			break;
		}
	}

	// Did not find the right session.  Return to the original location.
	if (!bFound)
		fsetpos(pFile, &originalPosition);

	// Cleanup
	delete [] buffer;

	// Return whether or not we were able to seek to the given session.
	return bFound;
}
bool packetLogger::loadSessionFromLog(string fileName, session* s, int sessionNumber)
{
	// Open the file...
	FILE *loadHandle = fopen(fileName.c_str(), "r");
	if (!loadHandle)
		return false;

	// Seek to the session if we can.  Otherwise fail.
	if (!seekToSession(loadHandle, sessionNumber))
	{
		fclose(loadHandle);
		return false;
	}

	// Have a way to store all of our data.  Make these persistent, so that
	// we do not constantly have to allocate and re-allocate.
	timedHeader h;
	const int			readBufSz = 1024;
	char				*readBuffer = new char[readBufSz];
	char				srcBuffer[30];
	char				dstBuffer[30];
	static int			srcPort;
	static int			dstPort;
	static int			payloadSize;
	static u_char		flags;
	bool				bFailure=false;

	// Read until we hit the end of the file...
	while (!feof(loadHandle))
	{
		// Read the line...
		if ( fgets(readBuffer, readBufSz, loadHandle) == NULL )
		{
			// bFailure = true;
			// break;
		}

		// If we are beginning to read a different session, stop.
		if (isSessionIdHeader(readBuffer))
			break;

		// Check to make sure the line is data we care about (not a comment)
		if (!isLogData(readBuffer))
			continue;

		// Read the data
		sscanf(readBuffer,
			   format_string(),
			   &h.timestamp.tv_sec,
			   &h.timestamp.tv_usec,
			   &h.getTCP()->th_seq,
			   &h.getTCP()->th_ack,
			   srcBuffer,
			   &srcPort,
			   dstBuffer,
			   &dstPort,
			   &payloadSize,
			   &flags);

		// Set the information on the header.
		h.setSrcIP(srcBuffer);
		h.setDstIP(dstBuffer);
		h.setSrcPort(srcPort);
		h.setDstPort(dstPort);
		h.setPayloadSize(payloadSize);
		h.setFlags(flags);

		// Load the packet into the session without analysis
		s->loadPacket(h);
	}

	// Set the session number
	s->setSessionID(sessionNumber);

	// Cleanup
	fclose(loadHandle);
	delete [] readBuffer;

	return !bFailure;
}
